home *** CD-ROM | disk | FTP | other *** search
/ Magnum One / Magnum One (Mid-American Digital) (Disc Manufacturing).iso / d18 / vmmngr.arc / VMM.DOC < prev    next >
Text File  |  1990-07-16  |  40KB  |  1,120 lines

  1.  
  2.  
  3.  
  4.                    ┌────────────────────────────┐
  5.                    │VMM - Virtual Memory Manager│
  6.                    └────────────────────────────┘
  7.  
  8.               A complementary unit for Object Professional
  9.                  a package from TurboPower Software
  10.  
  11.                   Author : Patrick Philippot - 7/90
  12.  
  13.  
  14.           ╔═══════════════════════════════════════════════════╗
  15.           ║This  package  is released to the public domain. It║
  16.           ║can  be  distributed  to  anyone FOR FREE. I DO NOT║
  17.           ║authorize  anyone  to  charge  ANY  AMOUNT of money║
  18.           ║for   this  package  wathever  the  media  used  to║
  19.           ║distribute it.                                     ║
  20.           ╚═══════════════════════════════════════════════════╝
  21.  
  22.            First  of  all  let me apologize for my rather poor
  23.            english.   I   hope   anyone  will  understand  the
  24.            following documentation.
  25.  
  26.            WARNING  : You need Turbo Professional version 1.0x
  27.            to compile this package.
  28.  
  29.                           *****************
  30.  
  31.  
  32.  
  33. A. Overview
  34.  
  35. The Virtual Memory Manager object (VMM) allows you to allocate memory for
  36. dynamic variables without being limited by the size of the Turbo Pascal
  37. heap. That is, VMM is not a replacement system for the Turbo Pascal memory
  38. manager but it provides you with an easy-to-use complementary package able
  39. to temporarily store allocated blocks in EMS or on disk and to automatically
  40. move them into RAM when they are needed.
  41.  
  42. VMM was written with the following specifications in mind:
  43.  
  44. - VMM methods do not replace the System.GetMem and System.FreeMem routines.
  45. They can peacefully coexist with VMM without any trouble. VMM memory blocks
  46. are allocated in a separate RAM area, allocated on the heap. This area does
  47. not interfere with the memory areas managed by Turbo Pascal itself.
  48.  
  49. - The VMM object is intended to be a complementary memory management
  50. system which will be used to allocate medium sized blocks of memory (or
  51. numerous smaller ones) that don't need to be present in RAM at any time.
  52.  
  53. - VMM does not require the Turbo Pascal compiler to be patched to generate
  54. a particular interrupt each time a pointer is dereferenced. Instead, it
  55. provides a dereferencing routine which is to be applied to each pointer
  56. returned by VMM. This will guarantee compatibility with future versions of
  57. Turbo Pascal.
  58.  
  59. - VMM is an object. This provides you with the general advantages related
  60. to objects but also with this one : you can generate as many objects of type
  61. VMM as you want, each of them dealing with different kind of data. This is
  62. very much like having several heaps enabled to use disk and EMS to swap out
  63. data, each of them being dedicated to a particular role in your application.
  64.  
  65. Using VMM
  66.  
  67. All the internal mechanisms of VMM are fully transparent to you and adding
  68. the capabilities of VMM to your applications is very straightforward.
  69.  
  70. First of all, like for many objects, you have to initialize the VMM
  71. object. This is done using one of two methods. The Init method initializes
  72. the VMM with default options which are suitable for most cases. You only
  73. have to provide the name of the swap file. The InitCustom method allows you
  74. to gain more control over the VMM object and to specify the initialization
  75. parameters.
  76.  
  77. Then you will allocate memory blocks using the GetMemV method and free them
  78. using the FreeMemV method. Dereferencing is made thru the VmmDrf inline
  79. macro. Dereferencing a pointer means that you make reference to the data it
  80. points to by adding a caret (^) after the name of the pointer.
  81.  
  82. When your program terminates or if you want to recover the space used in RAM,
  83. EMS and on disk by your VMM, you'll call the Done method.
  84.  
  85. That's all.
  86.  
  87. VMMTEST1 and VMMTEST2 are short programs showing you the whole process of
  88. initializing a VMM, allocating and freeing memory with it. Since they allocate
  89. a lot of virtual memory blocks they execute rather lengthy. They were used to
  90. debug the VMMNGR unit.
  91.  
  92.  
  93. B. Dynamic arrays
  94.  
  95. Although Dynamic Arrays are not specific to the VMM, we use them heavily
  96. in the VMM design. This allows VMM to use the smallest possible amount
  97. of RAM memory to store the structures needed to manage the allocated blocks.
  98. Dynamic Arrays can be used for many other purposes so we decided to include
  99. the related documentation in this section.
  100.  
  101. The DynArray object allows you to create arrays holding elements of any size,
  102. growing dynamically when a size increase is needed. DynArrays are very
  103. useful when the number of elements in the array is not known at compile time
  104. and when other dynamic structures like linked lists or queues are not
  105. relevant.
  106.  
  107. A DynArray is not as easy to use as a "normal" array because elements cannot
  108. be accessed directly. However this drawback is compensated by the fact that
  109. DynArrays are more generic and can hold elements of any type.
  110.  
  111. The total size of a dynamic array as well as the size of a single element
  112. cannot exceed 64k. More accurately, 65521 bytes.
  113.  
  114. Although this capability is not used by VMM objects, DynArrays can be
  115. stored to and loaded from streams.
  116.  
  117. EXDYNARR.PAS shows you how DynArrays work.
  118.  
  119. --------------------------------------------------------------------------------
  120. Init
  121.  
  122. Declaration
  123.   constructor Init(MaxElements, ElementSize, Incr : Word);
  124.  
  125. Purpose
  126.   Initializes an empty DynArray.
  127.  
  128. Description
  129.   Sets data area pointer to nil, item count to zero and initializes other data
  130.   from the passed parameters. MaxElements is the maximum number of items the
  131.   dynamic array may contain. ElementSize is the size of a single item. Incr is
  132.   the basis used to calculate the minimum size increment when inserting new
  133.   items. Since inserting a new item may cause the data area to be reallocated,
  134.   choosing a very small value for Incr will naturally save memory space but
  135.   will cause heap fragmentation. For example, giving Incr the value of 128
  136.   will cause space for 128 items to be allocated even if only the first item
  137.   has been inserted. Inserting the 129th item will cause space for 128
  138.   additional items to be allocated.
  139.  
  140.   No space is allocated for the array until an element is inserted.
  141.  
  142. Example
  143.     MyDynArray.Init(100, SizeOf(LongInt), 5);
  144.   Initializes a dynamic array containing at most 100 elements of type LongInt.
  145.   The size of the array will be increased by increments of 5*4 bytes.
  146.  
  147. --------------------------------------------------------------------------------
  148. Done
  149.  
  150. Declaration
  151.   destructor Done; virtual;
  152.  
  153. Purpose
  154.   Destroy a DynArray.
  155.  
  156. Description
  157.   Frees data area and resets data area pointer to nil.
  158.  
  159. Example
  160.   MyDynArray.Done;
  161.  
  162. See Also
  163.   Clear
  164.  
  165. --------------------------------------------------------------------------------
  166. SetElem
  167.  
  168. Declaration
  169.   procedure SetElem(Index : Word; var Elem);
  170.  
  171. Purpose
  172.   Move the contents of Elem in the indexth element of the DynArray.
  173.  
  174. Description
  175.  
  176. Valid indexes range from 0 to MaxElements minus 1. DynArrays are always 0
  177. based. Allocates enough space to the data area to hold at least Index
  178. elements. Elements which have not been set are filled with nulls. If Index
  179. is not a valid index, GetStatus returns epFatal+ecBadParam. If there is not
  180. enough memory available to allocate to the data area, GetStatus returns
  181. epFatal+ecOutOfMemory.
  182.  
  183. Index may range from 0 to GetMaxIndex.
  184.  
  185. Example
  186.     var
  187.       L : LongInt;
  188.  
  189.     L := 100000;
  190.     MyDynArray.SetElem(50, L);
  191.  
  192.   Puts the value 100000 in the 51th element of MyDynArray.
  193.  
  194. See Also
  195.   GetElem        GetValidElems      GetMaxIndex
  196.  
  197. --------------------------------------------------------------------------------
  198. GetElem
  199.  
  200. Declaration
  201.   procedure GetElem(Index : Word; var Elem);
  202.  
  203. Purpose
  204.   Puts the value of the Indexth element of array in Elem.
  205.  
  206. Description
  207.  
  208. Provided Index is a valid index, Elem will be loaded with the value
  209. contained in the Indexth element of the array. It is assumed that Elem is big
  210. enough to hold one element of the array. Otherwise memory will be overwritten.
  211.  
  212. Example
  213.     var
  214.       L : LongInt;
  215.  
  216.     MyDynArray.GetELem(20, L);
  217.     if L = TestValue then
  218.     ...
  219.   Puts value of 21th element of the array in L.
  220.  
  221. See Also
  222.   GetValidElems       SetElem         GetMaxIndex
  223.  
  224. --------------------------------------------------------------------------------
  225. GetElemSize
  226.  
  227. Declaration
  228.   function GetElemSize : Word;
  229.  
  230. Purpose
  231.   Return size of a single element.
  232.  
  233. Description
  234.   Returns size passed to the ElemSize parameter in the Init method.
  235.  
  236. Example
  237.     if MyDynArray.GetElemSize > 4 then
  238.     ...
  239.     Tests if size of one element is greater than 4 bytes.
  240.  
  241. See Also
  242.   Init
  243.  
  244. --------------------------------------------------------------------------------
  245. GetArraySize
  246.  
  247. Declaration
  248.   function GetArraySize : Word;
  249.  
  250. Purpose
  251.   Return the actual size in bytes allocated to the data area.
  252.  
  253. Description
  254.  
  255. As the elements are inserted the size of the dynamically allocated data area
  256. increases. GetArraySize return the size occupied by the data area on the
  257. heap independently from the number of elements actually used.
  258.  
  259. Example
  260.     var
  261.       L : LongInt;
  262.       S : Word;
  263.     MyDynArray.Init(100, SizeOf(LongInt), 20);
  264.     L := 100000
  265.     SetElem(0, L);
  266.     S := MyDynArray.GetArraySize;
  267.     {S now yields 20*4 bytes}
  268.     L := 200000
  269.     SetElem(1, L);
  270.     S := MyDynArray.GetArraySize;
  271.     {S still yields 80 bytes}
  272.  
  273. See Also
  274.   GetValidElems
  275.  
  276. --------------------------------------------------------------------------------
  277. GetMaxIndex
  278.  
  279. Declaration
  280.   function GetMaxIndex : Word;
  281.  
  282. Purpose
  283.   Return maximum admissible value for an index regardless of the actual size
  284.   of the data area.
  285.  
  286. Description
  287.   This is the greatest value acceptable for the Index parameter passed to the
  288.   Init method. It is equal to MaxElements-1.
  289.  
  290. Example
  291.     MyDynArray.Init(100, SizeOf(LongInt), 20);
  292.     I := MyDynArray.GetMaxIndex;
  293.     {I now yields 99}
  294.  
  295. --------------------------------------------------------------------------------
  296. GetValidElems
  297.  
  298. Declaration
  299.   function GetValidElems : Word;
  300.  
  301. Purpose
  302.   Return the maximum index that can be passed as a parameter to GetElem.
  303.  
  304. Description
  305.   Although the Init method specifies the greatest number of elements a dynamic
  306.   array may contain, no memory is allocated to hold all these elements unless
  307.   a call to SetElem is made for the latest element in the array. The actual
  308.   size of the data area is not a good information because we allocate memory
  309.   for it with minimum increments. So the GetElem method should not use indexes
  310.   greater than GetValidElems-1.
  311.  
  312. Example
  313.     var
  314.       L : LongInt;
  315.       I : Word;
  316.     MyDynArray.Init(100, SizeOf(LongInt), 20);
  317.     I := MyDynArray.GetMaxIndex;
  318.     {I = 99}
  319.     L := 100000
  320.     SetElem(0, L);
  321.     I := MyDynArray.GetValidElems;
  322.     {I = 1}
  323.     L := 200000
  324.     SetElem(9, L);
  325.     S := MyDynArray.GetValidElems;
  326.     {I = 10}
  327.  
  328. See Also
  329.   GetMaxIndex
  330.  
  331. --------------------------------------------------------------------------------
  332. Shrink
  333.  
  334. Declaration
  335.   procedure Shrink(ElemNb : Word);
  336.  
  337. Purpose
  338.   Decrease data area size and discard exceeding elements.
  339.  
  340. Description
  341.   Reallocate the data area and discard elements above index ElemNb-1. If
  342.   ElemNb is greater than GetValidElems nothing happens. This doesn't prevent
  343.   from setting any element between the first (index 0) and the last elements
  344.   (GetMaxIndex).
  345.  
  346. Example
  347.     MyDynArray.Shrink(50);
  348.  
  349. --------------------------------------------------------------------------------
  350. Clear
  351.  
  352. Declaration
  353.   procedure Clear;
  354.  
  355. Purpose
  356.   Reset the dynamic array and discard any data.
  357.  
  358. Description
  359.   Disposes of the data area and reset all internal variables. The dynamic array
  360.   is left in the same state as after the Init method has been run.
  361.  
  362. Example
  363.     MyDynArray.Clear;
  364.  
  365. --------------------------------------------------------------------------------
  366. Load
  367.  
  368. Declaration
  369.   constructor Load(var S : IdStream);
  370.  
  371. Purpose
  372.   Load the dynamic array from a stream.
  373.  
  374. Description
  375.   S is a properly intialized stream object.
  376.  
  377.   Load reads the next sequence of bytes from the stream S. These bytes must
  378.   have been written by a previous call to DynARray.Store. Load allocates heap
  379.   space for the data area, copy the data to that area and initializes all
  380.   internal variables. The dynamic array is left in the same state as it was
  381.   when the call to the Store method was made.
  382.  
  383.   if Load fails, the constructor will return False and the error code will
  384.   be stored in the global variable InitStatus.
  385.  
  386.   The stream registration procedure for a DynArray is DynArrayStream.
  387.  
  388. Example
  389.     MyDynArray.Load(S);
  390.  
  391. See Also
  392.   Store
  393.  
  394. --------------------------------------------------------------------------------
  395. Store
  396.  
  397. Declaration
  398.   constructor Store(var S : IdStream);
  399.  
  400. Purpose
  401.   Store the dynamic array to a stream.
  402.  
  403. Description
  404.   S is a properly intialized stream object.
  405.  
  406.   Store writes an image of the DynArray to the stream S. The image includes
  407.   the array data, the current number of valid elements, the maximum number of
  408.   elements, the size of the data area, the minimum grow increment, the maximum
  409.   number of elements and the size of a single element.
  410.  
  411.   To check for errors that have occured during the Store, call the stream's
  412.   GetStatus function.
  413.  
  414.   The stream registration procedure for a DynArray is DynArrayStream.
  415.  
  416. Example
  417.     MyDynArray.Store(S);
  418.  
  419. See Also
  420.   Load
  421. --------------------------------------------------------------------------------
  422. GetStatus
  423.  
  424. Declaration
  425.   function GetStatus : Word;
  426.  
  427. Purpose
  428.   Return status code and reset internal result to zero.
  429.  
  430. Description
  431.   Each DynArray maintains an internal variable that stores the current status of
  432.   the  dynamic array. GetStatus provides direct access to this variable. Like
  433.   IOResult, GetStatus clears the internal status variable after it returns the
  434.   current value.
  435.  
  436.   When no error has occured GetStatus returns 0. Otherwise it returns an error
  437.   code as specified in the Error handling section. Expected error values
  438.   include:
  439.  
  440.   epFatal+ecBadParam
  441.   epFatal+ecOutOfMemory
  442.  
  443. Example
  444.     MyDynArray.SetElem(10, V);
  445.     if GetStatus <> 0 then begin
  446.       {process error}
  447.       ...
  448.     end;
  449.  
  450. --------------------------------------------------------------------------------
  451. PeekStatus
  452.  
  453. Declaration
  454.   function PeekStatus : Word;
  455.  
  456. Purpose
  457.   Return status and leave internal variable unchanged.
  458.  
  459. Description
  460.   Works like GetStatus but doesn't clear the internal status variable.
  461.  
  462. --------------------------------------------------------------------------------
  463. Error
  464.  
  465. Declaration
  466.   procedure Error(Code : Word);
  467.  
  468. Purpose
  469.   Report an error.
  470.  
  471. Description
  472.   Applications do not call this routine directly. When a DynArray method
  473.   detects an error, it calls the error method to report it. The default
  474.   implementation of this method simply assigns the error code to an internal
  475.   status variable. An object derived from VMM may override the Error method
  476.   to provide different error handling behavior.
  477.  
  478. --------------------------------------------------------------------------------
  479.  
  480. C. The Virtual Memory Manager
  481.  
  482. Although VMM is very simple to use, it's important to have a look at the
  483. way it works. You will use VMMs more efficiently if you have some ideas
  484. about the GPVMM design.
  485.  
  486. VMM components are:
  487.  
  488. - the VMM pointers which are actually handles returned by the GetMemV
  489. procedure. These pointers have to be considered like normal pointers by the
  490. user except that they cannot be dereferenced directly. The VmmDrf inline
  491. macro has to be used to dereference such pointers.
  492.  
  493. - the RAM area which is used as a transfer area where memory blocks are
  494. first allocated and to which or from which these blocks are paged in and out
  495. if needed. When you allocate a block with the GetMemV procedure you can be
  496. sure that the block is present in the RAM area just after calling GetMemV.
  497. (in general you cannot assume that a particular block is present in memory).
  498. Later, if we need room in the RAM area to allocate a new block or to page in
  499. an older one, blocks present in the RAM area will be paged out using the LRU
  500. algorithm until enough memory space has been made free. The RAM area is
  501. allocated on the Turbo Pascal heap. It may be greater than 64k, provided you
  502. replace the standard GetMem procedure by a routine of your own. A hook is
  503. provided for that.
  504.  
  505. - the Freelists, which are used to keep track of free blocks in RAM, EMS and
  506. on disk. Freelists are all derived from the DynArray object type which has
  507. been described in the previous section. There are 3 freelists :
  508. vmRamFreeList, vmEmsFreeList and vmDskFreeList.
  509.  
  510. - the Descriptor Table, which is able to translate a VMM pointer returned
  511. by GetMemV into a record holding all the information about the memory block.
  512. A VMM Descriptor knows where a block is located, whether it is locked in
  513. RAM and where it is located on the actual media containing it (RAM, EMS or
  514. swap file). The Descriptor is also derived from the DynArray type.
  515.  
  516. - The LRU queue which keeps track of the Least Recently Used VMM pointers
  517. (handles). When paging out is needed, VMM first swaps out the blocks that
  518. were used (which pointers were dereferenced) least recently. This is
  519. achieved by pushing a VMM pointer (actually, only the handle part of it)
  520. into the LRU queue each time it created or dereferenced. If the VMM
  521. pointer already exists in the queue it is removed before adding it to the
  522. queue. The LRU queue is derived from the OPROOT StaticQueue object. It has
  523. two additional methods : IsEmpty which returns true if the queue is empty
  524. and Remove which is able to remove the fist occurence of an element in a
  525. StaticQueue updating data and head and tail pointers. VMM always try to
  526. remove a handle from the LRU queue before adding to the tail. This makes sure
  527. that the handle will be unique in the queue. Trying to page out the same
  528. handle twice would cause a system error.
  529.  
  530. - The dereference handler. This procedure (actually an interrupt handler) is
  531. not part of the object definition. Its name is DerefHandler and it is called
  532. by the VmmDrf inline macro. The interrupt vector used is 66h.
  533.  
  534. Interrupt 66h is one of the user-definable interrupts described by IBM. If
  535. this interrupt conflicts with your environment, you may change the global
  536. typed constant VmIntUsed to use a different interrupt. User-definable
  537. interrupts range from 60h to 66h. (67h is used for EMS, so it should be
  538. avoided here). If you change the interrupt, be sure to modify the VmmDrf
  539. inline function as well.
  540.  
  541. The interrupt vector is restored when your program exits or when a runtime
  542. error occurs.
  543.  
  544. Since the dereference handler does not know which VMM has generated the
  545. pointer which is to be dereferenced, it refers to a global pointer called
  546. VmmActiveMgr. This pointer is set by a special method, LinkToDerefHandler,
  547. which stores the value of the SELF pointer into VmmActiveMgr. This way, the
  548. dereference handler always knows which data to use. Failing to call
  549. LinkToDerefHandler before dereferencing a VMM pointer will likely cause
  550. your program to hang.
  551.  
  552. If you used GetMemV to allocate space for a dynamic record, you'll need to do
  553. some typecasting to access the different fields of the record. Example:
  554.  
  555. type
  556.   MyRec = record
  557.             I : Integer;
  558.             W : Word;
  559.             S : String;
  560.           end;
  561.   MyRecPtr = ^MyRec;
  562.  
  563. var
  564.   MyPtr = MyRecPtr;
  565.  
  566. ...
  567.   GetMemV(MyPtr, SizeOf(MyRec));
  568. ...
  569.   VmmDrf(MyPtr)^.S            {Will generate a compiler error}
  570.   MyRecPtr(VmmDrf(MyPtr))^.S  {Will compile}
  571.   MyRec(VmmDrf(MyPtr)^).S     {Will compile}
  572.  
  573.  
  574. When you request a memory allocation, the following process occurs.
  575.  
  576. First, the VMM object examines the RAM area to check if enough memory is
  577. available. If not, memory blocks will be paged out to EMS or disk until enough
  578. memory is available in the RAM area. Blocks are paged out to EMS first and
  579. then to disk. The VMM will not use all EMS or disk space available. You can
  580. control this by setting the EmsToKeep and DskToKeep parameters when calling
  581. InitCustom. The defaults used by the Init method are 1 megabyte disk space
  582. and 10% of the available Ems memory left free.
  583.  
  584. When memory blocks are dereferenced, the same mechanism occurs until the
  585. block can be loaded into the RAM area. Under some circumstances it may be
  586. possible that all virtual memory ressources (EMS and disk) are exhausted and
  587. that not enough free space is available in the RAM area. In order to prevent
  588. such a "dead lock", VMM refuses to allocate space if there is not enough
  589. space in EMS or on disk to page out 3 times the size of the RAM area. Keeping
  590. free only as many bytes as the RAM area can hold is not sufficient because of
  591. fragmentation and because we may have for example to swap out to disk blocks
  592. that have been primarily paged in from EMS.
  593.  
  594. VMM allows the RAM area to be greater than 64k. Though, since this area is
  595. allocated on the Turbo Pascal heap, it is not possible to use System.GetMem to
  596. achieve this. If you have replacement routines for System.GetMem and
  597. System.FreeMem, you can tell VMM to use them by setting the value of two
  598. global procedure variables : UserGetMem and UserFreeMem. These routines must
  599. be of type GetMemFUnc and FreeMemProc:
  600.  
  601.   GetMemFunc  = function(var P; Size : LongInt) : Boolean;
  602.   FreeMemProc = procedure(var P; Size : LongInt);
  603.  
  604. UserGetMem must return nil if no allocation was made.
  605. UserFreeMem must set the pointer to nil.
  606.  
  607. UserGetMem defaults to VmmGetMem and UserFreeMem defaults to VmmFreeMem which
  608. are revised versions of GetMemCheck and FreeMemCheck found in OpRoot.
  609.  
  610. VMM has very few options. You can only specify if you VMM to use only
  611. EMS virtual memory or only disk virtual memory. Disabling both ressources
  612. wouldn't make much sense. So it's impossible. Another option let you specify
  613. if the swap file has to be deleted when the VMM object is destroyed. This
  614. is the default option. Keeping the swap file on the disk will be sometimes
  615. useful for debugging purposes.
  616.  
  617. Both vmUseEms and vmUseDsk options are set to true unless the corresponding
  618. parameter of InitCustom explicitly excludes one of these ressources (empty
  619. name for the swap file or NoEms -$FFFF- constant for EmsToKeep). Lack of
  620. both ressources when a VMM is initialized causes the initialization
  621. process to fail. If either EMS or disk space are not present in sufficient
  622. amounts, the corresponding option is set to false. If your program later
  623. releases some EMS memory or delete some files you may try to activate the
  624. relevant option.
  625.  
  626. --------------------------------------------------------------------------------
  627. Init
  628.  
  629. Declaration
  630.   constructor Init(SwapFName : PathStr);
  631.  
  632. Purpose
  633.   Initialize a Virtual Memory Manager.
  634.  
  635. Description
  636.   Init initializes the VMM using default options. You only have to provide
  637.   the name of the swap file. Init calls InitCustom with the following
  638.   parameters:
  639.  
  640.   MaxHeapAlloc          = 65521 bytes
  641.   DefIncr               = 128 bytes
  642.   DefFreeEntries div 2  = 1024 entries / 4096 bytes
  643.   DefFreeEntries        = 2048 entries / 8192 bytes
  644.   DefQueueSize          = 1024-1 entries
  645.   DefEmsToKeep          = 10% of Ems pages available when Init is called
  646.   DefDskToKeep          = 1 megabyte is left free on swap disk
  647.  
  648.   Please refer to the description of InitCustom to have an explanation of the
  649.   meaning of these parameters. Using Init will be sufficient in most cases.
  650.  
  651.   Don't worry about the way you pass the swap filename. This name will be
  652.   expanded (via FExpand) to produce a full qualified unique filename.
  653.  
  654. Example
  655.     VM.Init('VMM.SWP');
  656.     VM.LinkToDerefHandler;
  657.  
  658. See Also
  659.   InitCustom           LinkToDerefHandler
  660.  
  661. --------------------------------------------------------------------------------
  662. InitCustom
  663.  
  664. Declaration
  665.   constructor InitCustom(RamSize : LongInt;
  666.                          Incr, MaxVmmEntries,
  667.                          MaxFreeEntries, VmmQueueEntries,
  668.                          EmsPagesToKeep     : Word;
  669.                          DskToKeep : LongInt;
  670.                          SwapFName : PathStr);
  671.  
  672. Purpose
  673.   Initialize a Virtual Memory Manager with custom options.
  674.  
  675. Description
  676.   InitCustom provides a more sophisticated way of initializing a VMM.
  677.  
  678.   RamSize specifies how much memory has to be allocated on the Turbo Pascal
  679.   heap for the RAM area. This size may be greater than 64k, provided
  680.   UserGetMem (see above) points to a routine allowing such a huge allocation.
  681.   Init uses a default of 65521 bytes.
  682.  
  683.   Incr is the increment used to initialize the dynamic arrays holding the
  684.   freelists. Please refer to the DynArray.Init method to understand the role
  685.   of this increment value.
  686.  
  687.   MaxVmmEntries specifies the maximum number of VMM pointers that can be
  688.   allocated by the memory manager. This doesn't mean that space is reserved
  689.   for all of these entries. Space is allocated on demand when entries are
  690.   created by GetMemV.
  691.  
  692.   MaxFreeEntries defines the maximum number of entries in the freelists.
  693.   Freelists do not need to be very big unless you generate a big number of
  694.   VMM pointers or you use FreeMemV very heavily. The default values of Init
  695.   will do in almost all circumstances.
  696.  
  697.   VmmQueueEntries specifies how many handles can be held in the LRU queue.
  698.   Because the RAM area is much smaller than the memory space available in EMS
  699.   or on disk, you don't need to set up a very big LRU queue. In theory, to be
  700.   sure that the paging process will never fail we should have as many entries
  701.   in the LRU queue as we can hold memory blocks in the RAM area. A very
  702.   extreme case would be allocating blocks of one byte. Assuming we would have
  703.   a RAM area of 65521 bytes, we would need 65521 entries in LRU queue! This is
  704.   impossible because each entry need two bytes. Most of the time a VMM will
  705.   deal with middle sized memory blocks. So using a default of 512 entries will
  706.   be sufficient.
  707.  
  708.   EmsPagesToKeep specifies how many EMS pages the VMM should leave free at
  709.   any time for the application program (or for another VMM). If you want to
  710.   use, say, 2 VMM in an application and you don't want to see the EMS
  711.   resource to be exhausted by the first VMM, leaving only disk virtual
  712.   memory for the second one, you can set the EmsToKeep parameter to a higher
  713.   value for the first initialized VMM than for the second one.
  714.  
  715.   If EmsToKeep equals NoEms ($FFFF) the vmUseEms option is deactivated. This
  716.   way the virtual memory manager will use only disk to page out memory blocks.
  717.  
  718.   DskToKeep specifies how much disk space the VMM should leave free at any
  719.   time for the application program (or for another VMM). The same remark as
  720.   for the EMS memory applies here too.
  721.  
  722.   SwapFName is the name of the swap file. If this name is an empty string, the
  723.   vmUseDsk option is deactivated.
  724.  
  725. Example
  726.     VM.InitCustom(30000, 50, 500, 500, 150, 50, 0, '');
  727.     VM.LinkToDerefHandler;
  728.  
  729.   Initializes VM with a RAM area of 30000 bytes, a minimum increment of 50
  730.   entries for the dynamic arrays, a maximum number of 500 entries in the
  731.   Descriptor Table (that is 500 VMM pointers) and in the freelists. 50 EMS
  732.   pages will be left free and the disk will not be used for paging out.
  733.  
  734. See Also
  735.   Init             LinkToDerefHandler
  736.  
  737. --------------------------------------------------------------------------------
  738. Done
  739.  
  740. Declaration
  741.   destructor Done; virtual;
  742.  
  743. Purpose
  744.   Destroy a virtual memory manager.
  745.  
  746. Description
  747.   All dynamic arrays used by the VMM are destroyed and the memory they used
  748.   is released to the heap. The RAM area memory is also released to the heap.
  749.   All EMS handles allocated by the VMM are deallocated. The swap file is
  750.   closed and deleted (unless the vmDeleteSwap option is off).
  751.  
  752. Example
  753.   VM.Done;
  754.  
  755. --------------------------------------------------------------------------------
  756. GetMemV
  757.  
  758. Declaration
  759.   procedure GetMemV(var Pt; BlkSize : Word);
  760.  
  761. Purpose
  762.   Allocate a memory block in RAM area and return a VMM pointer in Pt.
  763.  
  764. Description
  765.   GetMemV first searches for a free block of size BlkSize in the RAM area. If
  766.   it doesn't find such a block it pages out memory blocks present in the RAM
  767.   area (beginning with the Less Recently Used) until sufficient memory space
  768.   has been made free to allocate the new block. If this process fails, it
  769.   returns a nil pointer. Otherwise it returns a VMM pointer in Pt. This
  770.   pointer is not a real pointer. The offset part is always $FFFF and the
  771.   segment part is a handle which will be used to enter the Descriptor Table
  772.   where information about the allocated block is stored.
  773.  
  774.   The returned pointer cannot be used like a normal pointer. It must be
  775.   "dereferenced" thru the VmmDrf function.
  776.  
  777.   The new handle is added to the tail of the LRU queue.
  778.  
  779. Example
  780.     var
  781.       P : ^string;
  782.     ...
  783.     VM.GetMemV(P, Length(str)+1);
  784.     VmmDrf(P)^ := str;
  785.  
  786.   Allocates enough space to hold str.
  787.  
  788. See Also
  789.   FreeMemV      VmmDrf
  790.  
  791. --------------------------------------------------------------------------------
  792. FreeMemV
  793.  
  794. Declaration
  795.   procedure FreeMemV(var Pt);
  796.  
  797. Purpose
  798.   Release memory used by Pt.
  799.  
  800. Description
  801.   The memory block used by Pt is deallocated. The corresponding entry in the
  802.   Descriptotr Table is marked as a free entry. A new entry is inserted in the
  803.   relevant freelist. Pt is set to nil.
  804.  
  805.   You don't need to specify the size of the block because the Descriptor Table
  806.   keeps track of this. This is necessary to manage the freelists and much more
  807.   convenient for you.
  808.  
  809. Example
  810.     VM.FreeMemV(P);
  811.  
  812. See Also
  813.   GetMemV
  814.  
  815. --------------------------------------------------------------------------------
  816. LinkToDerefHandler
  817.  
  818. Declaration
  819.   procedure LinkToDerefHandler;
  820.  
  821. Purpose
  822.   Make the DerefHandler aware of which VMM is active.
  823.  
  824. Description
  825.   Since the dereference handler is called by an INT instruction, we cannot
  826.   specify to which VMM it has to refer. Only one VMM can be active at a
  827.   given time. It is the last one which issued a call to LinkToDerefHandler.
  828.   This one line procedure stores the value of the SELF pointer into a global
  829.   variable which will be referred to by the dereference handler.
  830.  
  831.   Using VMM.VmmDrf without linking the VMM to the dereference handler is
  832.   very much like using unitialized pointers. This will likely cause a program
  833.   failure.
  834.  
  835. Example
  836.     VM.LinkToDerefHandler;
  837.  
  838. See Also
  839.   VmmDrf
  840.  
  841. --------------------------------------------------------------------------------
  842. Lock
  843.  
  844. Declaration
  845.   function Lock(var Pt; Lockit : Boolean) : Boolean;
  846.  
  847. Purpose
  848.   Prevent a memory block from being paged out.
  849.   Allow a block to be paged out.
  850.  
  851. Description
  852.  
  853.   Lock updates the Descriptor Table entry corresponding to the passed VMM
  854.   pointer (Pt) and sets the "lock" bit if Lockit is true. The handle is
  855.   removed from the LRU queue. From now on, the block cannot be paged out any
  856.   more. It will remain in the RAM area. The block is unlocked by passing a
  857.   value of False in Lockit.
  858.  
  859.   Lock will be used to make sure that a block remains in memory even when
  860.   another pointer is dereferenced. Lock works even if the block pointed to by
  861.   Pt is presently not in RAM. If you dereference the corresponding pointer,
  862.   the block will be moved to the RAM area and will stay there until you unlock
  863.   it.
  864.  
  865.   Lock should be use with extreme care because locking one or several blocks
  866.   in the RAM area may cause the paging out process to fail if the dereferenced
  867.   pointer points to a block which is too big to fit into the remaining space.
  868.   You should make sure that the locked block(s) and the blocks that will be
  869.   paged into the RAM area have a total size smaller than the RAM area size. A
  870.   block should be locked in RAM for very short periods of time and unlocked as
  871.   soon as its presence in RAM is no more necessary.
  872.  
  873.   To help you managing this you may want to use the ClearRamArea method before
  874.   locking blocks.
  875.  
  876.   As we said a VMM is a complementary memory management system that should be
  877.   used only for temporarily storing medium sized data out of the heap not for
  878.   storing blocks that are used very often.
  879.  
  880. Example
  881.     if VM.Lock(MyPtr, true) then
  882.       ...
  883.  
  884. See Also
  885.   ClearRamArea
  886.  
  887. --------------------------------------------------------------------------------
  888. GetSize
  889.  
  890. Declaration
  891.   function GetSize(var Pt) : Word;
  892.  
  893. Purpose
  894.   Return the size of the memory block pointed to by Pt.
  895.  
  896. Description
  897.   GetSize looks into the Descriptor Table and returns the size stored in the
  898.   corresponding descriptor.
  899.  
  900. Example
  901.     S := VM.GetSize(MyPtr);
  902.  
  903. --------------------------------------------------------------------------------
  904. ClearRamArea
  905.  
  906. Declaration
  907.   function ClearRamArea : Boolean;
  908.  
  909. Purpose
  910.   Page out all non locked memory blocks.
  911.  
  912. Description
  913.   ClearRamArea is a simple call to the internal method called PageOut,
  914.   requesting the system to page out all blocks stored in the RAM area unless
  915.   they are locked. After a call to ClearRamArea, if there is no locked blocks
  916.   you can be sure that the entire RAM area is free of any data. If you want to
  917.   make sure that 2 or 3 blocks can be present in RAM simultaneaously, use
  918.   ClearRamArea before dereferencing the corresponding pointers. ClearRamArea
  919.   returns true if all the RAM area has been cleared.
  920.  
  921. Example
  922.     if not VM.ClearRamArea then
  923.       Writeln('Some blocks are still locked in RAM.');
  924.  
  925. See Also
  926.   Lock
  927.  
  928. --------------------------------------------------------------------------------
  929. RamMaxAvail
  930.  
  931. Declaration
  932.   function RamMaxAvail : LongInt;
  933.  
  934. Purpose
  935.   Return the size of the biggest free memory block in the RAM area.
  936.  
  937. Description
  938.   RamMaxAvail return the size of the last entry in the RamFreeList. Since all
  939.   freelists are always sorted in size order, the size of the last entry is the
  940.   size of the biggest free block.
  941.  
  942. Example
  943.     S := VM.RamMaxAvail;
  944.  
  945. --------------------------------------------------------------------------------
  946. EmsMaxAvail
  947.  
  948. Declaration
  949.   function EmsMaxAvail : LongInt;
  950.  
  951. Purpose
  952.   Return the size of the biggest free memory block in Ems.
  953.  
  954. Description
  955.   EmsMaxAvail return the size of the last entry in the EmsFreeList or 64k if
  956.   sufficient EMS is available to allocate a 4 pages frame. Since all freelists
  957.   are always sorted in size order, the size of the last entry is the size of
  958.   the biggest free block.
  959.  
  960. Example
  961.     S := VM.EmsMaxAvail;
  962.  
  963. --------------------------------------------------------------------------------
  964. DskMaxAvail
  965.  
  966. Declaration
  967.   function DskMaxAvail : LongInt;
  968.  
  969. Purpose
  970.   Return the size of the biggest free memory block on disk.
  971.  
  972. Description
  973.   DskMaxAvail return the size of the last entry in the DskFreeList or the
  974.   amount of disk space available minus DskToKeep. Since all freelists are
  975.   always sorted in size order, the size of the last entry is the size of the
  976.   biggest free block.
  977.  
  978. Example
  979.     S := VM.DskMaxAvail;
  980.  
  981. --------------------------------------------------------------------------------
  982. vmOptionsOn
  983.  
  984. Declaration
  985.   procedure vmOptionsOn(OptionFlags : Word);
  986.  
  987. Purpose
  988.   Turn specified VMM options on.
  989.  
  990. Description
  991.  
  992. Turning vmUseDsk or vmUseEms options has no effect if there is not enough disk
  993. or EMS space available.
  994.  
  995. Example
  996.     VM.vmOptionsOn(vmUseDsk)
  997.  
  998. --------------------------------------------------------------------------------
  999. vmOptionsOff
  1000.  
  1001. Declaration
  1002.   procedure vmOptionsOff(OptionFlags : Word);
  1003.  
  1004. Purpose
  1005.   Turn specified VMM options off.
  1006.  
  1007. Description
  1008.   Setting both vmUseDsk and vmUseEms options off is not possible. Doing so
  1009.   would cause VMM to work in RAM only, that is with very limited resources.
  1010.   For example, if you already set vmUseEms off and try to do the same with
  1011.   vmUseDsk, it will remain on.
  1012.  
  1013. Example
  1014.     VM.vmOptionsOff(vmDeleteSwap)
  1015.  
  1016. --------------------------------------------------------------------------------
  1017. vmOptionsAreOn
  1018.  
  1019. Declaration
  1020.   function vmOptionsAreOn(OptionFlags : Word) : Boolean;
  1021.  
  1022. Purpose
  1023.   Return TRUE if OptionCodes are enabled.
  1024.  
  1025. Description
  1026.   This routine allows to determine whether a single option or multiple options
  1027.   are on. The possible options for VMM are vmUseDsk, vmUseEms, vmDeleteSwap.
  1028.  
  1029. Example
  1030.     if VM.vmOptionsAreOn(vmUseEms) then
  1031.        {VM is using Ems...}
  1032.  
  1033. --------------------------------------------------------------------------------
  1034. GetStatus
  1035.  
  1036. Declaration
  1037.   function GetStatus : Word;
  1038.  
  1039. Purpose
  1040.   Return status code and reset internal result to zero.
  1041.  
  1042. Description
  1043.   Each VMM maintains an internal variable that stores the current status of
  1044.   the memory manager. GetStatus provides direct access to this variable. Like
  1045.   IOResult, GetStatus clears the internal status variable after it returns the
  1046.   current value.
  1047.  
  1048.   When no error has occured GetStatus returns 0. Otherwise it returns an error
  1049.   code as specified in the Error handling section. Expected error values
  1050.   include:
  1051.  
  1052.   epNonFatal+ecOutOfRamEntries
  1053.   epNonFatal+ecOutOfEmsEntries
  1054.   epNonFatal+ecOutOfDskEntries
  1055.   epNonFatal+ecCantFreeEms
  1056.   epFatal+ecOutOfDescEntries
  1057.   epFatal+ecBadParam
  1058.   epFatal+ecEmsAllocation
  1059.   epFatal+ecEmsPageMapping
  1060.   epFatal+any I/O error
  1061.  
  1062. Example
  1063.     if not VM.ClearRamArea then
  1064.       Error := GetStatus;
  1065.       {process error}
  1066.       ...
  1067.     end;
  1068.  
  1069. --------------------------------------------------------------------------------
  1070. PeekStatus
  1071.  
  1072. Declaration
  1073.   function PeekStatus : Word;
  1074.  
  1075. Purpose
  1076.   Return status and leave internal variable unchanged.
  1077.  
  1078. Description
  1079.   Works like GetStatus but doesn't clear the internal status variable.
  1080.  
  1081. --------------------------------------------------------------------------------
  1082. Error
  1083.  
  1084. Declaration
  1085.   procedure Error(Code : Word);
  1086.  
  1087. Purpose
  1088.   Report an error.
  1089.  
  1090. Description
  1091.   Applications do not call this routine directly. When a VMM method detects an
  1092.   error, it calls the error method to report it. The default implementation of
  1093.   this method simply assigns the error code to an internal status variable. An
  1094.   object derived from VMM may override the Error method to provide different
  1095.   error handling behavior.
  1096.  
  1097.  
  1098. D. Error handling
  1099.  
  1100. Beside the GetStatus and PeekStatus functions a VMM can generate runtime
  1101. errors. In some cases a program cannot recover from an error. If a bad
  1102. pointer is passed to VmmDrf or if dereferencing is not possible because the
  1103. EMS manager failed or because there are too much locked blocks in memory,
  1104. the only way out is a runtime error. This very much like runtime errors
  1105. occuring when the System.GetMem or System.FreeMem procedures fail.
  1106.  
  1107. Runtime errors generated by VMM are:
  1108.  
  1109.               211 : Abstract method not overridden (should not occur).
  1110.               212 : Non recoverable EMS error.
  1111.               213 : Could not page out or bad pointer.
  1112.               204 : Invalid pointer operation.
  1113. epFatal+I/O error : non recoverable disk error (only when dereferencing)
  1114.  
  1115. Before halting the program the VMM unit restores INT 66h. Another
  1116. mechanism which is not processed by the Exit procedure, deallocates all EMS
  1117. handles allocated by all VMMs before calling the RunError procedure.
  1118.  
  1119. <Patrick Philippot - 16/7/90>
  1120.